package itpenguin.train.mta;

import itpenguin.train.common.GtfsRealTimeFeed;
import itpenguin.train.common.Position;
import itpenguin.train.common.Train;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.google.protobuf.ExtensionRegistry;
import com.google.transit.realtime.GtfsRealtime.FeedEntity;
import com.google.transit.realtime.GtfsRealtime.FeedMessage;
import com.google.transit.realtime.GtfsRealtime.TripUpdate;
import com.google.transit.realtime.GtfsRealtime.TripUpdate.StopTimeUpdate;
import com.google.transit.realtime.GtfsRealtime.VehiclePosition;
import com.google.transit.realtime.NyctSubway;
import com.google.transit.realtime.NyctSubway.NyctTripDescriptor;
import com.google.transit.realtime.NyctSubway.NyctTripDescriptor.Direction;

public class Mta extends GtfsRealTimeFeed {
    
    private static final String API_KEY = "ef94ae2ce1e1d01ffe17624dfb8d233c";

    @Override
    public List<Train> getCurrentPositions() throws IOException {
        
        List<Train> positions = new ArrayList<Train>();
        
        ExtensionRegistry registry = ExtensionRegistry.newInstance();
        registry.add(NyctSubway.nyctFeedHeader);
        registry.add(NyctSubway.nyctStopTimeUpdate);
        registry.add(NyctSubway.nyctTripDescriptor);
        fetchFeed(positions, registry, new URL("http://datamine.mta.info/mta_esi.php?key=" + API_KEY + "&feed_id=1"), StopIdType.Include);
        fetchFeed(positions, registry, new URL("http://datamine.mta.info/mta_esi.php?key=" + API_KEY + "&feed_id=2"), StopIdType.Exclude);
        fetchFeed(positions, registry, new URL("http://datamine.mta.info/mta_esi.php?key=" + API_KEY + "&feed_id=11"), StopIdType.ExcludeNoDir);
        
        return positions;
        
    }

	private enum StopIdType {
		Include,
		Exclude,
		ExcludeNoDir
	}
	
    private void fetchFeed(List<Train> positions, ExtensionRegistry registry, URL url,
    		StopIdType stopIdType) throws IOException {
        
    	FeedMessage fm = FeedMessage.parseFrom(url.openStream(), registry);
        Map<String, String> stopIdDic = null;
        
        if (stopIdType != StopIdType.Include) {
        	stopIdDic = new HashMap<String, String>();
            for (FeedEntity entity : fm.getEntityList()) {
            	if (entity.hasTripUpdate()) {
            		TripUpdate tu = entity.getTripUpdate();
            		if (tu.getStopTimeUpdateCount() > 0) {
            			StopTimeUpdate stu = tu.getStopTimeUpdate(0);
                        NyctTripDescriptor ntd = tu.getTrip().getExtension(NyctSubway.nyctTripDescriptor);
                        String stopId;
                        if (stopIdType == StopIdType.ExcludeNoDir)
                        	stopId = stu.getStopId() + (ntd.getDirection() == Direction.NORTH ? "N" : "S");
                        else
                        	stopId = stu.getStopId();
            			stopIdDic.put(tu.getTrip().getTripId(), stopId);
            		}
            	}
            }
        }
        for (FeedEntity entity : fm.getEntityList()) {
            if (entity.hasVehicle()) {
                VehiclePosition vp = entity.getVehicle();
                NyctTripDescriptor ntd = vp.getTrip().getExtension(NyctSubway.nyctTripDescriptor);
                String stopId;
                if (stopIdType != StopIdType.Include)
                	stopId = stopIdDic.get(vp.getTrip().getTripId());
                else
                	stopId = vp.getStopId();
                Position position = Dictionary.getStationPosition(stopId);
                if (position == null) {
                    continue;
                }
                Train tp = new Train(position.getLatitude(), position.getLongitude(), vp.getCurrentStatus()
                        ,ntd.getTrainId() , vp.getTrip().getTripId(), vp.getTrip().getRouteId(),
                        new Date(vp.getTimestamp() * 1000));
                positions.add(tp);
            }
        }
    }
    
}
